import 'package:flutter/services.dart';
import 'package:local_auth/local_auth.dart';
// ignore: depend_on_referenced_packages
import 'package:local_auth_android/local_auth_android.dart';
// ignore: depend_on_referenced_packages
import 'package:local_auth_darwin/local_auth_darwin.dart';
import 'package:my_flutter_core/utils/ui_interaction_utils.dart';

/// local_auth package (https://pub.dev/packages/local_auth) which needs FragmentActivity in MainActivity.
/// But FragmentActivity causes shared_preferences package (https://pub.dev/packages/shared_preferences) fail on running.
///
/// flutter_local_auth_invisible can work with FlutterActivity instead of FragmentActivity.
/// The document says there is a default UI but I did not see it.
/// That means I have to implement it by myself.
class LocalAuthenticationService {
  LocalAuthenticationService._();

  static final instance = LocalAuthenticationService._();

  final LocalAuthentication _localAuth = LocalAuthentication();

  /// Returns true if device is capable of checking biometrics.
  ///
  /// Returns a [Future] bool true or false:
  Future<bool> get canCheckBiometrics async {
    return _localAuth.canCheckBiometrics;
  }

  /// Returns true if device is capable of checking biometrics or is able to fail over to device credentials.
  ///
  /// Returns a [Future] bool true or false:
  Future<bool> get isDeviceSupported async {
    return _localAuth.isDeviceSupported();
  }

  /// Returns true if device is capable of checking fingerprint.
  ///
  /// Can use it to determine if need to enable fingerprint feature.
  ///
  /// Returns a [Future] bool true or false:
  Future<bool> get canCheckFingerprint async {
    final canCheck = await canCheckBiometrics || await isDeviceSupported;
    var result = false;

    if (canCheck) {
      final availableBiometrics = await _localAuth.getAvailableBiometrics();

      if (availableBiometrics.isNotEmpty &&
          (availableBiometrics.contains(BiometricType.fingerprint) ||
              availableBiometrics.contains(BiometricType.strong) ||
              availableBiometrics.contains(BiometricType.weak))) {
        result = true;
      }
    }

    return result;
  }

  /// Authenticates the user with biometrics available on the device.
  ///
  /// Returns a [Future] holding true, if the user successfully authenticated,
  /// false otherwise.
  ///
  /// [localizedReason] is the message to show to user while prompting them
  /// for authentication. This is typically along the lines of: 'Please scan
  /// your finger to access MyApp.'
  Future<bool> authenticateWithFingerprint({
    String localizedReason = '需要验证指纹',
    String biometricHint = '',
  }) async {
    const signInTitle = '需要验证指纹';
    const signInHint = '';
    const cancelButtonText = '取消';
    const localizedFallbackTitle = '指纹识别';
    const lockedOutCode = 'lockedout';

    var result = false;

    try {
      // [stickyAuth] is used when the application goes into background for any
      // reason while the authentication is in progress. Due to security reasons,
      // the authentication has to be stopped at that time. If stickyAuth is set
      // to true, authentication resumes when the app is resumed. If it is set to
      // false (default), then as soon as app is paused a failure message is sent
      // back to Dart and it is up to the client app to restart authentication or
      // do something else.
      result = await _localAuth.authenticate(
        localizedReason: localizedReason,
        authMessages: [
          AndroidAuthMessages(
            signInTitle: signInTitle,
            signInHint: signInHint,
            cancelButton: cancelButtonText,
          ),
          const IOSAuthMessages(
            localizedFallbackTitle: localizedFallbackTitle,
            cancelButton: cancelButtonText,
          ),
        ],
      );
    } on PlatformException catch (e) {
      cancelAuthentication();

      if (e.code.toLowerCase() == lockedOutCode) {
        showToast('因为多次尝试失败，请稍后重试');
      } else {
        showToast('请稍后重试');
      }
    }

    return result;
  }

  /// Cancels the in-progress authentication.
  void cancelAuthentication() {
    _localAuth.stopAuthentication();
  }
}
